﻿using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Reflection;
using RAP.Framework.Libary.DataSource.Model;
using RAP.Framework.Libary.DataSource.Model.Attribute;
using RAP.Framework.Libary.DataSource.Model.Setter;
using RAP.Framework.Libary.Lock;
using RAP.Framework.Libary.Utils;
using RAP.Framework.Libary.Utils.Grouped;

namespace RAP.Framework.Libary.DataSource.DataReader
{
  /// <summary>
  /// 模型对象解析器，将数据库查询的结行结果转换成指定类型的实体
  /// </summary>
  public class DataModelParser : DataReaderParser, IDataModelParser
  {
    private static bool _modelTypesInitialized = false;

    /// <summary>
    /// 模型类型[Type]缓存
    /// </summary>
    private static IDictionary<string, Type> _dataModelTypeCaches = new ConcurrentDictionary<string, Type>();

    /// <summary>
    /// 模型属性[PropertyInfo]缓存
    /// </summary>
    private static IDictionary<Type, IDictionary<string, IPropertyValueSetter>> _dataModelPropertySetterCaches = new ConcurrentDictionary<Type, IDictionary<string, IPropertyValueSetter>>();

    /// <summary>
    /// 
    /// </summary>
    public DataModelParser() { }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="assFilter"></param>
    public DataModelParser(Func<Assembly, bool> assFilter)
    {
      AssemblyFilter = assFilter;
    }

    /// <summary>
    /// 程序集过滤规则
    /// </summary>
    public Func<Assembly, bool> AssemblyFilter { get; set; } = (ass) =>
    {
      return !ass.FullName.StartsWithAny("System", "Microsoft", "Yahoo", "Aspose", "mscorlib");
    };

    /// <summary>
    /// 实例化指定类型的实体(IDataModel)对象
    /// </summary>
    /// <param name="typeName"></param>
    /// <returns></returns>
    public virtual IDataModel CreateDataModel(string typeName)
    {
      var type = GetDataModelType(typeName);
      return CreateDataModel(type);
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="type"></param>
    /// <returns></returns>
    public virtual IDataModel CreateDataModel(Type type)
    {
      return DataSourceHelper.DataModelGenerator.CreateProxyClass(type);
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="resultTypeName"></param>
    /// <param name="properites"></param>
    /// <returns></returns>
    public override object CreateDataModel(string resultTypeName = null, IDictionary properites = null)
    {
      var data = CreateDataModel(resultTypeName);
      if (data != null && properites.IsNotNullOrEmpty())
      {
        var setters = GetPropertyValueSetters(data);
        foreach (DictionaryEntry ety in properites)
        {
          SetPropertyValue(data, setters, ety.Key.ToString(), ety.Value);
        }
      }
      return data;
    }

    /// <summary>
    /// 根据类型完全名称或别名获取数据实体类型
    /// </summary>
    /// <param name="typeName"></param>
    /// <returns></returns>
    public virtual Type GetDataModelType(string typeName)
    {
      Type type = null;
      if (typeName.LastIndexOf(',') > 0)
      {
        //如果是完全限定名
        if (!_dataModelTypeCaches.TryGetValue(typeName, out type))
        {
          type = Type.GetType(typeName);
          if (type == null)
          {
            throw new Exception($"模型的数据类型（{typeName}）错误！");
          }
          _dataModelTypeCaches.Add(typeName, type);
        }
      }
      else
      {
        //在所有程序集中查找模型对象类型
        //查找所有IDataModel对象并缓存
        if (!_dataModelTypeCaches.TryGetValue(typeName, out type))
        {
          lock (this)
          {
            if (!_dataModelTypeCaches.TryGetValue(typeName, out type) && !_modelTypesInitialized)
            {
              InitDataModeTypes();
            }
          }
        }
        if (!_dataModelTypeCaches.TryGetValue(typeName, out type))
        {
          throw new Exception($"模型的数据类型（{typeName}）错误！");
        }
      }
      return type;
    }

    /// <summary>
    /// 初始化实体模型类型
    /// </summary>
    protected virtual void InitDataModeTypes(string interfaceName = "IDataModel")
    {
      if (_modelTypesInitialized)
      {
        return;
      }
      AppDomain.CurrentDomain.GetAssemblies().Map<Assembly>(ass =>
      {
        if (!AssemblyFilter(ass))
        {
          return;
        }
        try
        {
          var assTypes = ass.GetTypes();
          var types = from t in assTypes
                      where t.GetInterface(interfaceName) != null
                      select t;
          foreach (var t in types)
          {
            if (t.IsAbstract || t.IsSealed || !t.IsClass || t.IsNotPublic)
            {
              continue;
            }
            var attr = t.GetCustomAttribute<ModelNameAttribute>();
            var name = t.Name;
            if (attr != null)
            {
              name = attr.Name.IsNullOrEmptyValue(name);
            }
            _dataModelTypeCaches.TryAdd(name, t);//查找到类型之后立即缓存
          }
        }
        catch (Exception ex)
        {
          throw ex;
        }
      });
      _modelTypesInitialized = true;
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="dataModelType"></param>
    /// <param name="property"></param>
    /// <param name="propertyType"></param>
    /// <returns></returns>
    public virtual IPropertyValueSetter CreatePropertyValueSetter(Type dataModelType, string property, Type propertyType)
    {
      return new PropertyValueSetter(dataModelType, property, propertyType);
    }

    /// <summary>
    /// 获取指定类型的参数复制表达式集合
    /// </summary>
    /// <param name="dataModelType"></param>
    /// <returns></returns>
    public virtual IDictionary<string, IPropertyValueSetter> GetPropertyValueSetters(IDataModel dataModel)
    {
      var dataModelType = dataModel.GetType();
      if (!_dataModelPropertySetterCaches.TryGetValue(dataModelType, out var propertySetterCaches))
      {
        lock (dataModelType)
        {
          if (!_dataModelPropertySetterCaches.TryGetValue(dataModelType, out propertySetterCaches))
          {
            propertySetterCaches = new Dictionary<string, IPropertyValueSetter>();
            dataModelType.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.SetProperty).Map<PropertyInfo>(p =>
            {
              if (p.CanWrite)
              {
                propertySetterCaches.Add(p.Name, CreatePropertyValueSetter(dataModelType, p.Name, p.PropertyType));
              }
            });
            _dataModelPropertySetterCaches.Add(dataModelType, propertySetterCaches);
          }
        }
      }
      return propertySetterCaches;
    }

    /// <summary>
    /// 设置IDataModel对象的属性值
    /// </summary>
    /// <param name="dataModel"></param>
    /// <param name="property"></param>
    /// <param name="value"></param>
    public virtual void SetPropertyValue(IDataModel dataModel, string property, object value)
    {
      SetPropertyValue(dataModel, GetPropertyValueSetters(dataModel), property, value);
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="setters"></param>
    /// <param name="property"></param>
    /// <param name="value"></param>
    public virtual void SetPropertyValue(IDataModel dataModel, IDictionary<string, IPropertyValueSetter> setters, string property, object value)
    {
      var type = dataModel.GetType();
      if (setters.TryGetValue(property, out var propertySetter))
      {
        //如果属性存在，则对属性赋值
        propertySetter.SetValue(dataModel, value);
      }
      else if (dataModel is IDictionary)
      {
        dataModel.SetValue(property, value);
      }
      else if (property.IndexOf('.') > 0)
      {
        //如果是多级属性，且是第一次赋值
        GlobalLocks.Lock($"{type.FullName}.{property}", () =>
        {
          return setters.TryGetValue(property, out propertySetter);
        }, () =>
        {
          //构造该多级属性的PropertyValueSetter
          propertySetter = CreatePropertyValueSetter(type, property, type.GetPropertyType(property));
          setters.Add(property, propertySetter);
        });
        propertySetter.SetValue(dataModel, value);
      }
    }

    /// <summary>
    /// 将DataReader当前结果集转换成键值对集合
    /// </summary>
    /// <param name="dataReader"></param>
    /// <param name="pickFields"></param>
    /// <param name="typeName">结果类型名称或别名</param>
    /// <param name="parseContext">解析时用到的上下文对象</param>
    /// <returns></returns>
    public override ParseResult ParseData(
      IDataReader dataReader,
      IList pickFields,
      string typeName,
      DataParseContext parseContext)
    {
      var fields = this.GetFields(dataReader, pickFields);
      var onlyGetFirstData = pickFields.IsNotNullOrEmpty();
      var modelType = GetDataModelType(typeName);
      var list = onlyGetFirstData ? null : typeof(List<>).MakeGenericType(modelType).CreateInstance<IList>();//构建模型类型的泛型集合;
      while (dataReader.Read())
      {
        var data = CreateDataModel(modelType);
        data.SetContext(parseContext);
        var setters = GetPropertyValueSetters(data);
        for (var i = 0; i < fields.Count; i++)
        {
          var field = fields[i];
          SetPropertyValue(data, setters, field.Name, GetDBValue(dataReader, field));
        }

        if (onlyGetFirstData)
        {
          return new ParseResult
          {
            DataValue = data,
            DataCount = 1
          };
        }
        list.Add(data);
      }
      return new ParseResult
      {
        DataValue = parseContext.ModelParams.IsNullOrEmpty() ? (object)list : list.ToGroupedDictionary(parseContext.ModelParams),
        DataCount = list != null ? list.Count : 0
      };
    }
  }
}
